#pragma once
#include <core/container/KeyHashMap.h>
#include <mutex>
/*
	[内存优化][scofined.qi]
	增加ShortDate,取代原先4个字节的Date,用来表示一个年月日信息
	对年月日的年份有要求,必须是1980-2107
	对于现有市场的债券,够用,适合才是最好的
*/
#ifndef  FMTBUFN
#ifdef WIN32
#define FMTBUFN(buf,capcity,fmt,...)		_snprintf_s(buf,capcity,-1,fmt,##__VA_ARGS__)
#else
#define FMTBUFN(buf,capcity,fmt,...)		snprintf(buf,capcity,fmt,##__VA_ARGS__)
#endif
#endif

template<int CATEGORY, int CAPCITY>
class DateStringMap{
public:

	enum { PTR_LENGTH = 9,
		YEAR_BASE = 1980,
		YEAR_MAX = YEAR_BASE+127
	};
	typedef unsigned short ushort;
	typedef unsigned char  uchar;
	typedef DateStringMap<CATEGORY, CAPCITY> ThisType;

	struct ShortDate{
		ushort	year : 7;		// 以1980开始,支持最大127年,即到2107年
		ushort	month : 4;		// 1-12 只需要4个bit	
		ushort	day : 5;		// 1-31 只需要5个bit
	public:
		ShortDate() :year(0), month(0), day(0){}
		ShortDate(ushort y, uchar m, uchar d) :year(y), month(m), day(d){}
		ShortDate(unsigned int date){ //20171010
			day = date % 100;
			year = (date / 10000)-YEAR_BASE;
			month = (date - ((unsigned int)year + YEAR_BASE) * 10000) / 100;
		}
		int GetYear()const{ return (unsigned int)year+YEAR_BASE; }
		bool operator!=(const ShortDate& date)const{
			return year != date.year || month != date.month || day != date.day;
		}
		bool operator==(const ShortDate& d)const{ return year == d.year && month == d.month && day == d.day; }
		unsigned int GetHash()const{ return ((unsigned int)year << 16) | ((unsigned int)month << 8) | day; }
	};
	struct Date{
		ushort year;
		uchar month;
		uchar day;
	public:
		Date() :year(0), month(0), day(0){}
		Date(ushort y, uchar m, uchar d) :year(y), month(m), day(d){}
		Date(unsigned int date){ //20171010
			int y = date / 10000;
			BASSERT(y >= YEAR_BASE && y < YEAR_MAX);
			day = date % 100;
			year = date / 10000;
			month = (date - year * 10000) / 100;
		}
		int GetYear()const{ return year; }
		bool operator!=(const ShortDate& date)const{
			return year != date.year || month != date.month || day != date.day;
		}
		bool operator==(const Date& d)const{ return year == d.year && month == d.month && day == d.day; }
		unsigned int GetHash()const{ return (year << 16) | (month << 8) | day; }
	};
	typedef ShortDate DateType;
	struct Entry{
		Entry*		hashprev;			// 哈西插槽的双链表的后一个
		Entry*		hashnext;			// 哈西插槽的双链表的前一个
		DateType	key;				// 用于标志
		char*		ptr;
		int			count;
		Entry() :hashprev(0), hashnext(0), ptr(0), count(0){}
	};
	typedef const char* RawPtr;
	typedef qb::base::KeyHashMap<Entry, DateType> EntryDateHashMap;
	class EntryEraser : public EntryDateHashMap::Listener{
	public:
		virtual void OnEntry(Entry* entry, int slot){
			if (entry){
				if (entry->ptr)	delete[] entry->ptr;
				delete entry;
			}
		}
	};
protected:
	EntryDateHashMap	m_date_map;
	int					m_memory_bytes;
	int					m_category;
	int					m_capcity;
	std::mutex			m_lock;
public:
	static ThisType& Get(){
		static ThisType instance;
		return instance;
	}
	DateStringMap() :m_memory_bytes(0), m_category(CATEGORY), m_capcity(CAPCITY)
	{
		m_memory_bytes = sizeof(ThisType) - sizeof(EntryDateHashMap);
		m_date_map.Expand(CAPCITY); 
		//BVIEW("DateStringMap instance 0x:%8x  CATEGORY:%d CAPCITY:%d", this, CATEGORY, CAPCITY);
	}
	~DateStringMap()
	{
		EntryEraser eraser;
		m_date_map.Enum(&eraser);
	}
	int Memory()const{
		std::lock_guard<std::mutex> locker(const_cast<ThisType*>(this)->m_lock);
		int bytes = m_memory_bytes + m_date_map.GetBytes();
		return bytes;
	}
	int Category()const{ return CATEGORY; }
	const char* Load(const DateType& date)
	{
		RawPtr ptr = NULL;
		DateType d(date);
		std::lock_guard<std::mutex> locker(m_lock);
		Entry* entry = m_date_map.Find(d, d.GetHash());
		if (entry == NULL)
		{
			entry = new Entry();
			entry->key = d;
			entry->ptr = new char[PTR_LENGTH];
			entry->ptr[PTR_LENGTH - 1] = 0;
			if (date == 0)
				memset(entry->ptr, 0, PTR_LENGTH);
			else
				FMTBUFN(entry->ptr, PTR_LENGTH, "%04d%02d%02d", d.GetYear(), d.month, d.day);
			m_date_map.Insert(entry, d.GetHash());
			m_memory_bytes += sizeof(Entry) + PTR_LENGTH;
		}
		return entry->ptr;
	}
	DateType Save(const char* str, int len = 0)
	{
		// str 格式:"20171010" 
		// '0' '9'
		unsigned int num = atoi(str);
		return num;
	}
};

